﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Numerics;
using System.Diagnostics;

namespace BitcoinLibrary
{
    /**
     * A Wallet stores keys and a record of transactions that have not yet been spent. Thus, it is capable of
     * providing transactions on demand that meet a given combined value.<p>
     *
     * The Wallet is read and written from disk, so be sure to follow the Java serialization versioning rules here. We
     * use the built in Java serialization to avoid the need to pull in a potentially large (code-size) third party
     * serialization library.<p>
     */
    public class Wallet /*implements Serializable */
    {
        //private static readonly Logger log = LoggerFactory.getLogger(Wallet.class);
        private static readonly long serialVersionUID = 2L;

        // Algorithm for movement of transactions between pools. Outbound tx = us spending coins. Inbound tx = us
        // receiving coins. If a tx is both inbound and outbound (spend with change) it is considered outbound for the
        // purposes of the explanation below.
        //
        // 1. Outbound tx is created by us: ->pending
        // 2. Outbound tx that was broadcast is accepted into the main chain:
        //     <-pending  and
        //       If there is a change output  ->unspent
        //       If there is no change output ->spent
        // 3. Outbound tx that was broadcast is accepted into a side chain:
        //     ->inactive  (remains in pending).
        // 4. Inbound tx is accepted into the best chain:
        //     ->unspent/spent
        // 5. Inbound tx is accepted into a side chain:
        //     ->inactive
        //     Whilst it's also 'pending' in some sense, in that miners will probably try and incorporate it into the
        //     best chain, we don't mark it as such here. It'll eventually show up after a re-org.
        // 6. Outbound tx that is pending shares inputs with a tx that appears in the main chain:
        //     <-pending ->dead
        //
        // Re-orgs:
        // 1. Tx is present in old chain and not present in new chain
        //       <-unspent/spent  ->pending
        //       These newly inactive transactions will (if they are relevant to us) eventually come back via receive()
        //       as miners resurrect them and re-include into the new best chain.
        // 2. Tx is not present in old chain and is present in new chain
        //       <-inactive  and  ->unspent/spent
        // 3. Tx is present in new chain and shares inputs with a pending transaction, including those that were resurrected
        //    due to point (1)
        //       <-pending ->dead
        //
        // Balance:
        // 1. Sum up all unspent outputs of the transactions in unspent.
        // 2. Subtract the inputs of transactions in pending.
        // 3. If requested, re-add the outputs of pending transactions that are mine. This is the estimated balance.

        /**
         * Map of txhash->Transactions that have not made it into the best chain yet. They are eligible to move there but
         * are waiting for a miner to send a block on the best chain including them. These transactions inputs count as
         * spent for the purposes of calculating our balance but their outputs are not available for spending yet. This
         * means after a spend, our balance can actually go down temporarily before going up again!
         */
        internal readonly Dictionary<Sha256Hash, Transaction> pending;

        /**
         * Map of txhash->Transactions where the Transaction has unspent outputs. These are transactions we can use
         * to pay other people and so count towards our balance. Transactions only appear in this map if they are part
         * of the best chain. Transactions we have broacast that are not confirmed yet appear in pending even though they
         * may have unspent "change" outputs.<p>
         *
         * Note: for now we will not allow spends of transactions that did not make it into the block chain. The code
         * that handles this in BitCoin C++ is complicated. Satoshis code will not allow you to spend unconfirmed coins,
         * however, it does seem to support dependency resolution entirely within the context of the memory pool so
         * theoretically you could spend zero-conf coins and all of them would be included together. To simplify we'll
         * make people wait but it would be a good improvement to resolve this in future.
         */
        internal readonly Dictionary<Sha256Hash, Transaction> unspent;

        /**
         * Map of txhash->Transactions where the Transactions outputs are all fully spent. They are kept separately so
         * the time to create a spend does not grow infinitely as wallets become more used. Some of these transactions
         * may not have appeared in a block yet if they were created by us to spend coins and that spend is still being
         * worked on by miners.<p>
         *
         * Transactions only appear in this map if they are part of the best chain.
         */
        internal readonly Dictionary<Sha256Hash, Transaction> spent;

        /**
         * An inactive transaction is one that is seen only in a block that is not a part of the best chain. We keep it
         * around in case a re-org promotes a different chain to be the best. In this case some (not necessarily all)
         * inactive transactions will be moved out to unspent and spent, and some might be moved in.<p>
         *
         * Note that in the case where a transaction appears in both the best chain and a side chain as well, it is not
         * placed in this map. It's an error for a transaction to be in both the inactive pool and unspent/spent.
         */
        private Dictionary<Sha256Hash, Transaction> inactive;

        /**
         * A dead transaction is one that's been overridden by a double spend. Such a transaction is pending except it
         * will never confirm and so should be presented to the user in some unique way - flashing red for example. This
         * should nearly never happen in normal usage. Dead transactions can be "resurrected" by re-orgs just like any
         * other. Dead transactions are not in the pending pool.
         */
        private Dictionary<Sha256Hash, Transaction> dead;

        /** A list of public/private EC keys owned by this user. */
        public readonly List<ECKey> keychain;

        private readonly NetworkParameters param;

        /*transient */
        private List<WalletEventListener> eventListeners;

        /**
         * Creates a new, empty wallet with no keys and no transactions. If you want to restore a wallet from disk instead,
         * see loadFromFile.
         */
        public Wallet(NetworkParameters param)
        {
            this.param = param;
            keychain = new List<ECKey>();
            unspent = new Dictionary<Sha256Hash, Transaction>();
            spent = new Dictionary<Sha256Hash, Transaction>();
            inactive = new Dictionary<Sha256Hash, Transaction>();
            pending = new Dictionary<Sha256Hash, Transaction>();
            dead = new Dictionary<Sha256Hash, Transaction>();
            eventListeners = new List<WalletEventListener>();
        }

        /**
         * Uses Java serialization to save the wallet to the given file.
         */
        public /*synchronized */void saveToFile(FileStream fs)
        {
            saveToFileStream(fs);
        }

        /**
         * Uses Java serialization to save the wallet to the given file stream.
         */
        public /*synchronized */void saveToFileStream(FileStream f)
        {
            // TODO: save with serializer and data contract attributes?
            throw new Exception("NYI");
            //fObjectOutputStream oos = new ObjectOutputStream(f);
            //oos.writeObject(this);
            //oos.close();
        }


        /**
         * Returns a wallet deserialized from the given file.
         */
        public static Wallet loadFromFile(FileStream fs)
        {
            // TODO: save with serializer and data contract attributes?
            return loadFromFileStream(fs);
        }

        /**
         * Returns a wallet deserialied from the given file input stream.
         */
        public static Wallet loadFromFileStream(FileStream f)
        {
            // TODO: save with serializer and data contract attributes?
            throw new Exception("NYI");
            //ObjectInputStream ois = null;
            //try {
            //    ois = new ObjectInputStream(f);
            //    return (Wallet) ois.readObject();
            //} catch (ClassNotFoundException e) {
            //    throw new RuntimeException(e);
            //} readonlyly {
            //    if (ois != null) ois.close();
            //}
        }

        private void readObject(FileStream fs)
        {
            // TODO: save with serializer and data contract attributes?
            throw new Exception("NYI");
            //fs.defaultReadObject();
            //eventListeners = new List<WalletEventListener>();
        }

        /**
         * Called by the {@link BlockChain} when we receive a new block that sends coins to one of our addresses or
         * spends coins from one of our addresses (note that a single transaction can do both).<p>
         *
         * This is necessary for the internal book-keeping Wallet does. When a transaction is received that sends us
         * coins it is added to a pool so we can use it later to create spends. When a transaction is received that
         * consumes outputs they are marked as spent so they won't be used in future.<p>
         *
         * A transaction that spends our own coins can be received either because a spend we created was accepted by the
         * network and thus made it into a block, or because our keys are being shared between multiple instances and
         * some other node spent the coins instead. We still have to know about that to avoid accidentally trying to
         * double spend.<p>
         *
         * A transaction may be received multiple times if is included into blocks in parallel chains. The blockType
         * parameter describes whether the containing block is on the main/best chain or whether it's on a presently
         * inactive side chain. We must still record these transactions and the blocks they appear in because a future
         * block might change which chain is best causing a reorganize. A re-org can totally change our balance!
         */
        /*synchronized */
        public void receive(Transaction tx, StoredBlock block, BlockChain.NewBlockType blockType)
        {
            receive(tx, block, blockType, false);
        }

        private /*synchronized*/ void receive(Transaction tx, StoredBlock block,
                                          BlockChain.NewBlockType blockType, bool reorg)
        {
            // Runs in a peer thread.
            BigInteger prevBalance = getBalance();

            Sha256Hash txHash = tx.getHash();

            bool bestChain = blockType == BlockChain.NewBlockType.BEST_CHAIN;
            bool sideChain = blockType == BlockChain.NewBlockType.SIDE_CHAIN;

            BigInteger valueSentFromMe = tx.getValueSentFromMe(this);
            BigInteger valueSentToMe = tx.getValueSentToMe(this);
            BigInteger valueDifference = valueSentToMe - valueSentFromMe;

            if (!reorg)
            {
                // TODO: Debug.WriteLine("Received tx{} for {} BTC: {}", new Object[] { sideChain ? " on a side chain" : "",
                //        bitcoinValueToFriendlyString(valueDifference), tx.getHashAsString()});
            }

            // If this transaction is already in the wallet we may need to move it into a different pool. At the very
            // least we need to ensure we're manipulating the canonical object rather than a duplicate.
            Transaction wtx = null;
            //if ((wtx = pending.Remove(txHash)) != null) {
            if (pending.ContainsKey(txHash))
            {
                wtx = pending[txHash];
                // TODO: Debug.WriteLine("  <-pending");
                // A transaction we created appeared in a block. Probably this is a spend we broadcast that has been
                // accepted by the network.
                //
                // Mark the tx as appearing in this block so we can find it later after a re-org.
                wtx.addBlockAppearance(block);
                if (bestChain)
                {
                    if (valueSentToMe.Equals(BigInteger.Zero))
                    {
                        // There were no change transactions so this tx is fully spent.
                        // TODO: Debug.WriteLine("  ->spent");
                        bool alreadyPresent = spent.ContainsKey(wtx.getHash()); //spent.put(wtx.getHash(), wtx) != null;
                        Debug.Assert(!alreadyPresent, "TX in both pending and spent pools");
                    }
                    else
                    {
                        // There was change back to us, or this tx was purely a spend back to ourselves (perhaps for
                        // anonymization purposes).
                        // TODO: Debug.WriteLine("  ->unspent");
                        bool alreadyPresent = unspent.ContainsKey(wtx.getHash());   //unspent.put(wtx.getHash(), wtx) != null;
                        Debug.Assert(!alreadyPresent, "TX in both pending and unspent pools");
                    }
                }
                else if (sideChain)
                {
                    // The transaction was accepted on an inactive side chain, but not yet by the best chain.
                    // TODO: Debug.WriteLine("  ->inactive");
                    // It's OK for this to already be in the inactive pool because there can be multiple independent side
                    // chains in which it appears:
                    //
                    //     b1 --> b2
                    //        \-> b3
                    //        \-> b4 (at this point it's already present in 'inactive'
                    bool alreadyPresent = inactive.ContainsKey(wtx.getHash());// inactive.put(wtx.getHash(), wtx) != null;
                    if (alreadyPresent)
                        Debug.WriteLine("Saw a transaction be incorporated into multiple independent side chains");
                    // Put it back into the pending pool, because 'pending' means 'waiting to be included in best chain'.
                    pending.Add(wtx.getHash(), wtx);
                }
            }
            else
            {
                if (!reorg)
                {
                    // Mark the tx as appearing in this block so we can find it later after a re-org.
                    tx.addBlockAppearance(block);
                }
                // This TX didn't originate with us. It could be sending us coins and also spending our own coins if keys
                // are being shared between different wallets.
                if (sideChain)
                {
                    // TODO: Debug.WriteLine("  ->inactive");
                    inactive.Add(tx.getHash(), tx);
                }
                else if (bestChain)
                {
                    processTxFromBestChain(tx);
                }
            }

            Debug.WriteLine("Balance is now: " + Utils.bitcoinValueToFriendlyString(getBalance()));

            // Inform anyone interested that we have new coins. Note: we may be re-entered by the event listener,
            // so we must not make assumptions about our state after this loop returns! For example,
            // the balance we just received might already be spent!
            if (!reorg && bestChain && valueDifference.CompareTo(BigInteger.Zero) > 0)
            {
                foreach (WalletEventListener l in eventListeners)
                {
                    lock (l)
                    {
                        l.onCoinsReceived(this, tx, prevBalance, getBalance());
                    }
                }
            }
        }

        /**
         * Handle when a transaction becomes newly active on the best chain, either due to receiving a new block or a
         * re-org making inactive transactions active.
         */
        private void processTxFromBestChain(Transaction tx)
        {
            // This TX may spend our existing outputs even though it was not pending. This can happen in unit
            // tests and if keys are moved between wallets.
            updateForSpends(tx);
            if (!tx.getValueSentToMe(this).Equals(BigInteger.Zero))
            {
                // It's sending us coins.
                // TODO: Debug.WriteLine("  new tx ->unspent");
                bool alreadyPresent = unspent.ContainsKey(tx.getHash()); // unspent.Add(tx.getHash(), tx) != null;
                unspent.Add(tx.getHash(), tx);
                Debug.Assert(!alreadyPresent, "TX was received twice");
            }
            else
            {
                // It spent some of our coins and did not send us any.
                // TODO: Debug.WriteLine("  new tx ->spent");
                bool alreadyPresent = spent.ContainsKey(tx.getHash());  //spent.put(tx.getHash(), tx) != null;
                Debug.Assert(!alreadyPresent, "TX was received twice");
            }
        }

        /**
         * Updates the wallet by checking if this TX spends any of our outputs. This is not used normally because
         * when we receive our own spends, we've already marked the outputs as spent previously (during tx creation) so
         * there's no need to go through and do it again.
         */
        private void updateForSpends(Transaction tx)
        {
            foreach (TransactionInput input in tx.inputs)
            {
                TransactionInput.ConnectionResult result = input.connect(unspent, false);
                if (result == TransactionInput.ConnectionResult.NO_SUCH_TX)
                {
                    // Doesn't spend any of our outputs or is coinbase.
                    continue;
                }
                else if (result == TransactionInput.ConnectionResult.ALREADY_SPENT)
                {
                    // Double spend! This must have overridden a pending tx, or the block is bad (contains transactions
                    // that illegally double spend: should never occur if we are connected to an honest node).
                    //
                    // Work backwards like so:
                    //
                    //   A  -> spent by B [pending]
                    //     \-> spent by C [chain]
                    Transaction doubleSpent = input.outpoint.fromTx;   // == A
                    Transaction connected = doubleSpent.outputs[(int)input.outpoint.index].getSpentBy().parentTransaction;
                    if (pending.ContainsKey(connected.getHash()))
                    {
                        Debug.WriteLine("Saw double spend from chain override pending tx {}", connected.getHashAsString());
                        Debug.WriteLine("  <-pending ->dead");
                        pending.Remove(connected.getHash());
                        dead.Add(connected.getHash(), connected);
                        // Now forcibly change the connection.
                        input.connect(unspent, true);
                        // Inform the event listeners of the newly dead tx.
                        foreach (WalletEventListener listener in eventListeners)
                        {
                            lock (listener)
                            {
                                listener.onDeadTransaction(connected, tx);
                            }
                        }
                    }
                }
                else if (result == TransactionInput.ConnectionResult.SUCCESS)
                {
                    // Otherwise we saw a transaction spend our coins, but we didn't try and spend them ourselves yet.
                    // The outputs are already marked as spent by the connect call above, so check if there are any more for
                    // us to use. Move if not.
                    Transaction connected = input.outpoint.fromTx;
                    if (connected.getValueSentToMe(this, false).Equals(BigInteger.Zero))
                    {
                        // There's nothing left I can spend in this transaction.
                        if (unspent.Remove(connected.getHash()) != null)
                        {
                            Debug.WriteLine("  prevtx <-unspent");
                            Debug.WriteLine("  prevtx ->spent");
                            spent.Add(connected.getHash(), connected);
                        }
                    }
                }
            }
        }

        /**
         * Adds an event listener object. Methods on this object are called when something interesting happens,
         * like receiving money.<p>
         *
         * Threading: Event listener methods are dispatched on library provided threads and the both the wallet and the
         * listener objects are locked during dispatch, so your listeners do not have to be thread safe. However they
         * should not block as the Peer will be unresponsive to network traffic whilst your listener is running.
         */
        public /*synchronized*/ void addEventListener(WalletEventListener listener)
        {
            eventListeners.Add(listener);
        }

        /**
         * Call this when we have successfully transmitted the send tx to the network, to update the wallet.
         */
        /*synchronized*/
        void confirmSend(Transaction tx)
        {
            Debug.Assert(!pending.ContainsValue(tx), "confirmSend called on the same transaction twice");
            Debug.WriteLine("confirmSend of {}", tx.getHashAsString());
            // Mark the outputs of the used transcations as spent, so we don't try and spend it again.
            foreach (TransactionInput input in tx.inputs)
            {
                TransactionOutput connectedOutput = input.outpoint.getConnectedOutput();
                connectedOutput.markAsSpent(input);
            }
            // Some of the outputs probably send coins back to us, eg for change or because this transaction is just
            // consolidating the wallet. Mark any output that is NOT back to us as spent. Then add this TX to the
            // pending pool.
            foreach (TransactionOutput output in tx.outputs)
            {
                if (!output.isMine(this))
                {
                    // This output didn't go to us, so by definition it is now spent.
                    output.markAsSpent(null);
                }
            }
            pending.Add(tx.getHash(), tx);
        }

        /**
         * Statelessly creates a transaction that sends the given number of nanocoins to address. The change is sent to
         * the first address in the wallet, so you must have added at least one key.<p>
         *
         * This method is stateless in the sense that calling it twice with the same inputs will result in two
         * Transaction objects which are equal. The wallet is not updated to track its pending status or to mark the
         * coins as spent until confirmSend is called on the result.
         */
        /*synchronized*/
        Transaction createSend(Address address, BigInteger nanocoins)
        {
            // For now let's just pick the first key in our keychain. In future we might want to do something else to
            // give the user better privacy here, eg in incognito mode.
            Debug.Assert(keychain.Count > 0, "Can't send value without an address to use for receiving change");
            ECKey first = keychain[0];
            return createSend(address, nanocoins, first.toAddress(param));
        }

        /**
         * Sends coins to the given address, via the given {@link Peer}. Change is returned to the first key in the wallet.
         * @param to Which address to send coins to.
         * @param nanocoins How many nanocoins to send. You can use Utils.toNanoCoins() to calculate this.
         * @return The {@link Transaction} that was created or null if there was insufficient balance to send the coins.
         * @throws IOException if there was a problem broadcasting the transaction
         */
        public /*synchronized*/ Transaction sendCoins(Peer peer, Address to, BigInteger nanocoins)
        {
            Transaction tx = createSend(to, nanocoins);
            if (tx == null)   // Not enough money! :-(
                return null;
            peer.broadcastTransaction(tx);
            confirmSend(tx);
            return tx;
        }

        /**
         * Creates a transaction that sends $coins.$cents BTC to the given address.<p>
         *
         * IMPORTANT: This method does NOT update the wallet. If you call createSend again you may get two transactions
         * that spend the same coins. You have to call confirmSend on the created transaction to prevent this,
         * but that should only occur once the transaction has been accepted by the network. This implies you cannot have
         * more than one outstanding sending tx at once.
         *
         * @param address The BitCoin address to send the money to.
         * @param nanocoins How much currency to send, in nanocoins.
         * @param changeAddress Which address to send the change to, in case we can't make exactly the right value from
         * our coins. This should be an address we own (is in the keychain).
         * @return a new {@link Transaction} or null if we cannot afford this send.
         */
        /*synchronized*/
        Transaction createSend(Address address, BigInteger nanocoins, Address changeAddress)
        {
            Debug.WriteLine("Creating send tx to " + address.ToString() + " for " +
                    Utils.bitcoinValueToFriendlyString(nanocoins));
            // To send money to somebody else, we need to do gather up transactions with unspent outputs until we have
            // sufficient value. Many coin selection algorithms are possible, we use a simple but suboptimal one.
            // TODO: Sort coins so we use the smallest first, to combat wallet fragmentation and reduce fees.
            BigInteger valueGathered = BigInteger.Zero;
            List<TransactionOutput> gathered = new List<TransactionOutput>();
            foreach (Transaction tx in unspent.Values)
            {
                foreach (TransactionOutput output in tx.outputs)
                {
                    if (!output.isAvailableForSpending()) continue;
                    if (!output.isMine(this)) continue;
                    gathered.Add(output);
                    valueGathered = valueGathered + output.getValue();
                }
                if (valueGathered.CompareTo(nanocoins) >= 0) break;
            }
            // Can we afford this?
            if (valueGathered.CompareTo(nanocoins) < 0)
            {
                Debug.WriteLine("Insufficient value in wallet for send, missing " +
                        Utils.bitcoinValueToFriendlyString(nanocoins - valueGathered));
                // TODO: Should throw an exception here.
                return null;
            }
            Debug.Assert(gathered.Count > 0);
            Transaction sendTx = new Transaction(param);
            sendTx.addOutput(new TransactionOutput(param, sendTx, nanocoins, address));
            BigInteger change = valueGathered - nanocoins;
            if (change.CompareTo(BigInteger.Zero) > 0)
            {
                // The value of the inputs is greater than what we want to send. Just like in real life then,
                // we need to take back some coins ... this is called "change". Add another output that sends the change
                // back to us.
                Debug.WriteLine("  with " + Utils.bitcoinValueToFriendlyString(change) + " coins change");
                sendTx.addOutput(new TransactionOutput(param, sendTx, change, changeAddress));
            }
            foreach (TransactionOutput output in gathered)
            {
                sendTx.addInput(output);
            }

            // Now sign the inputs, thus proving that we are entitled to redeem the connected outputs.
            try
            {
                sendTx.signInputs(Transaction.SigHash.ALL, this);
            }
            catch (ScriptException e)
            {
                // If this happens it means an output script in a wallet tx could not be understood. That should never
                // happen, if it does it means the wallet has got into an inconsistent state.
                throw e;
            }
            Debug.WriteLine("  created {}", sendTx.getHashAsString());
            return sendTx;
        }

        /**
         * Adds the given ECKey to the wallet. There is currently no way to delete keys (that would result in coin loss).
         */
        public /*synchronized*/ void addKey(ECKey key)
        {
            Debug.Assert(!keychain.Contains(key));
            keychain.Add(key);
        }

        /**
         * Locates a keypair from the keychain given the hash of the public key. This is needed when finding out which
         * key we need to use to redeem a transaction output.
         * @return ECKey object or null if no such key was found.
         */
        public /*synchronized */ ECKey findKeyFromPubHash(byte[] pubkeyHash)
        {
            foreach (ECKey key in keychain)
            {
                if (Array.Equals(key.getPubKeyHash(), pubkeyHash)) return key;
            }
            return null;
        }

        /** Returns true if this wallet contains a public key which hashes to the given hash. */
        public /*synchronized */ bool isPubKeyHashMine(byte[] pubkeyHash)
        {
            return findKeyFromPubHash(pubkeyHash) != null;
        }

        /**
         * Locates a keypair from the keychain given the raw public key bytes.
         * @return ECKey or null if no such key was found.
         */
        public /*synchronized */ ECKey findKeyFromPubKey(byte[] pubkey)
        {
            foreach (ECKey key in keychain)
            {
                if (Array.Equals(key.getPubKey(), pubkey)) return key;
            }
            return null;
        }

        /** Returns true if this wallet contains a keypair with the given public key. */
        public  /*synchronized*/ bool isPubKeyMine(byte[] pubkey)
        {
            return findKeyFromPubKey(pubkey) != null;
        }

        /**
         * It's possible to calculate a wallets balance from multiple points of view. This enum selects which
         * getBalance() should use.<p>
         *
         * Consider a real-world example: you buy a snack costing $5 but you only have a $10 bill. At the start you have
         * $10 viewed from every possible angle. After you order the snack you hand over your $10 bill. From the
         * perspective of your wallet you have zero dollars (AVAILABLE). But you know in a few seconds the shopkeeper
         * will give you back $5 change so most people in practice would say they have $5 (ESTIMATED).<p>
         */
        public enum BalanceType
        {
            /**
             * Balance calculated assuming all pending transactions are in fact included into the best chain by miners.
             * This is the right balance to show in user interfaces.
             */
            ESTIMATED,

            /**
             * Balance that can be safely used to create new spends. This is all confirmed unspent outputs minus the ones
             * spent by pending transactions, but not including the outputs of those pending transactions.
             */
            AVAILABLE
        };

        /**
         * Returns the AVAILABLE balance of this wallet. See {@link BalanceType#AVAILABLE} for details on what this
         * means.<p>
         *
         * Note: the estimated balance is usually the one you want to show to the end user - however attempting to
         * actually spend these coins may result in temporary failure. This method returns how much you can safely
         * provide to {@link Wallet#createSend(Address, java.math.BigInteger)}.
         */
        public /*synchronized*/ BigInteger getBalance()
        {
            return getBalance(BalanceType.AVAILABLE);
        }

        /**
         * Returns the balance of this wallet as calculated by the provided balanceType.
         */
        public /*synchronized*/ BigInteger getBalance(BalanceType balanceType)
        {
            BigInteger available = BigInteger.Zero;
            foreach (Transaction tx in unspent.Values)
            {
                foreach (TransactionOutput output in tx.outputs)
                {
                    if (!output.isMine(this)) continue;
                    if (!output.isAvailableForSpending()) continue;
                    available = available + output.getValue();
                }
            }
            if (balanceType == BalanceType.AVAILABLE)
                return available;
            Debug.Assert(balanceType == BalanceType.ESTIMATED);
            // Now add back all the pending outputs to assume the transaction goes through.
            BigInteger estimated = available;
            foreach (Transaction tx in pending.Values)
            {
                foreach (TransactionOutput output in tx.outputs)
                {
                    if (!output.isMine(this)) continue;
                    estimated = estimated + output.getValue();
                }
            }
            return estimated;
        }

        public override string ToString()
        {
            StringBuilder builder = new StringBuilder();
            builder.Append("Wallet containing ");
            builder.Append(Utils.bitcoinValueToFriendlyString(getBalance()));
            builder.Append("BTC in ");
            builder.Append(unspent.Count);
            builder.Append(" unspent transactions/");
            builder.Append(spent.Count);
            builder.Append(" spent transactions");
            // Do the keys.
            builder.Append("\nKeys:\n");
            foreach (ECKey key in keychain)
            {
                builder.Append("  addr:");
                builder.Append(key.toAddress(param));
                builder.Append(" ");
                builder.Append(key.ToString());
                builder.Append("\n");
            }
            return builder.ToString();
        }

        /**
         * Called by the {@link BlockChain} when the best chain (representing total work done) has changed. In this case,
         * we need to go through our transactions and find out if any have become invalid. It's possible for our balance
         * to go down in this case: money we thought we had can suddenly vanish if the rest of the network agrees it
         * should be so.<p>
         *
         * The oldBlocks/newBlocks lists are ordered height-wise from top first to bottom last.
         */
        /*synchronized*/
        public void reorganize(List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks)
        {
            // This runs on any peer thread with the block chain synchronized.
            //
            // The reorganize functionality of the wallet is tested in ChainSplitTests.
            //
            // For each transaction we track which blocks they appeared in. Once a re-org takes place we have to find all
            // transactions in the old branch, all transactions in the new branch and find the difference of those sets.
            //
            // receive() has been called on the block that is triggering the re-org before this is called.

            Debug.WriteLine("  Old part of chain (top to bottom):");
            foreach (StoredBlock b in oldBlocks) Debug.WriteLine("    {}", b.getHeader().getHashAsString());
            Debug.WriteLine("  New part of chain (top to bottom):");
            foreach (StoredBlock b in newBlocks) Debug.WriteLine("    {}", b.getHeader().getHashAsString());

            // Transactions that appear in the old chain segment.
            Dictionary<Sha256Hash, Transaction> oldChainTransactions = new Dictionary<Sha256Hash, Transaction>();
            // Transactions that appear in the old chain segment and NOT the new chain segment.
            Dictionary<Sha256Hash, Transaction> onlyOldChainTransactions = new Dictionary<Sha256Hash, Transaction>();
            // Transactions that appear in the new chain segment.
            Dictionary<Sha256Hash, Transaction> newChainTransactions = new Dictionary<Sha256Hash, Transaction>();
            // Transactions that don't appear in either the new or the old section, ie, the shared trunk.
            Dictionary<Sha256Hash, Transaction> commonChainTransactions = new Dictionary<Sha256Hash, Transaction>();

            Dictionary<Sha256Hash, Transaction> all = new Dictionary<Sha256Hash, Transaction>();
            //all.putAll(unspent);
            //all.putAll(spent);
            //all.putAll(inactive);
            foreach (var item in unspent) all.Add(item.Key, item.Value);
            foreach (var item in spent) all.Add(item.Key, item.Value);   // TODO: ok (3 rows)
            foreach (var item in inactive) all.Add(item.Key, item.Value);

            foreach (Transaction tx in all.Values)
            {
                HashSet<StoredBlock> appearsIn = tx.getAppearsIn();
                Debug.Assert(appearsIn != null);
                // If the set of blocks this transaction appears in is disjoint with one of the chain segments it means
                // the transaction was never incorporated by a miner into that side of the chain.
                bool inOldSection = appearsIn.Except(oldBlocks).Count() > 0;    // TODO: ok ???? !Collections.disjoint(appearsIn, oldBlocks);
                bool inNewSection = appearsIn.Except(newBlocks).Count() > 0;    // TODO: ok ???? !Collections.disjoint(appearsIn, newBlocks);
                bool inCommonSection = !inNewSection && !inOldSection;

                if (inCommonSection)
                {
                    bool alreadyPresent = commonChainTransactions.ContainsKey(tx.getHash());    // commonChainTransactions.Add(tx.getHash(), tx) != null;
                    Debug.Assert(!alreadyPresent, "Transaction appears twice in common chain segment");
                    commonChainTransactions.Add(tx.getHash(), tx);
                }
                else
                {
                    if (inOldSection)
                    {
                        bool alreadyPresent = oldChainTransactions.ContainsKey(tx.getHash()); // oldChainTransactions.put(tx.getHash(), tx) != null;
                        Debug.Assert(!alreadyPresent, "Transaction appears twice in old chain segment");
                        oldChainTransactions.Add(tx.getHash(), tx);
                        if (!inNewSection)
                        {
                            alreadyPresent = onlyOldChainTransactions.ContainsKey(tx.getHash()); //alreadyPresent = onlyOldChainTransactions.put(tx.getHash(), tx) != null);
                            Debug.Assert(!alreadyPresent, "Transaction appears twice in only-old map");
                            onlyOldChainTransactions.Add(tx.getHash(), tx);
                        }
                    }
                    if (inNewSection)
                    {
                        bool alreadyPresent = newChainTransactions.ContainsKey(tx.getHash()); //bool alreadyPresent = newChainTransactions.put(tx.getHash(), tx) != null);
                        Debug.Assert(!alreadyPresent, "Transaction appears twice in new chain segment");
                        newChainTransactions.Add(tx.getHash(), tx);
                    }
                }
            }

            // If there is no difference it means we have nothing we need to do and the user does not care.
            bool affectedUs = !oldChainTransactions.Equals(newChainTransactions);
            Debug.WriteLine(affectedUs ? "Re-org affected our transactions" : "Re-org had no effect on our transactions");
            if (!affectedUs) return;

            // For simplicity we will reprocess every transaction to ensure it's in the right bucket and has the right
            // connections. Attempting to update each one with minimal work is possible but complex and was leading to
            // edge cases that were hard to fix. As re-orgs are rare the amount of work this implies should be manageable
            // unless the user has an enormous wallet. As an optimization fully spent transactions buried deeper than
            // 1000 blocks could be put into yet another bucket which we never touch and assume re-orgs cannot affect.

            foreach (Transaction tx in onlyOldChainTransactions.Values) Debug.WriteLine("  Only Old: {}", tx.getHashAsString());
            foreach (Transaction tx in oldChainTransactions.Values) Debug.WriteLine("  Old: {}", tx.getHashAsString());
            foreach (Transaction tx in newChainTransactions.Values) Debug.WriteLine("  New: {}", tx.getHashAsString());

            // Break all the existing connections.
            foreach (Transaction tx in all.Values)
                tx.disconnectInputs();
            foreach (Transaction tx in pending.Values)
                tx.disconnectInputs();
            // Reconnect the transactions in the common part of the chain.
            foreach (Transaction tx in commonChainTransactions.Values)
            {
                TransactionInput badInput = tx.connectInputs(all, false);
                Debug.Assert(badInput == null, "Failed to connect " + tx.getHashAsString() + ", " + badInput.ToString());
            }
            // Recalculate the unspent/spent buckets for the transactions the re-org did not affect.
            unspent.Clear();
            spent.Clear();
            inactive.Clear();
            foreach (Transaction tx in commonChainTransactions.Values)
            {
                int unspentOutputs = 0;
                foreach (TransactionOutput output in tx.outputs)
                {
                    if (output.isAvailableForSpending()) unspentOutputs++;
                }
                if (unspentOutputs > 0)
                {
                    Debug.WriteLine("  TX {}: ->unspent", tx.getHashAsString());
                    unspent.Add(tx.getHash(), tx);
                }
                else
                {
                    Debug.WriteLine("  TX {}: ->spent", tx.getHashAsString());
                    spent.Add(tx.getHash(), tx);
                }
            }
            // Now replay the act of receiving the blocks that were previously in a side chain. This will:
            //   - Move any transactions that were pending and are now accepted into the right bucket.
            //   - Connect the newly active transactions.
            newBlocks.Reverse();  // Need bottom-to-top but we get top-to-bottom.
            foreach (StoredBlock b in newBlocks)
            {
                Debug.WriteLine("Replaying block {}", b.getHeader().getHashAsString());
                HashSet<Transaction> txns = new HashSet<Transaction>();
                foreach (Transaction tx in newChainTransactions.Values)
                {
                    if (tx.appearsIn.Contains(b))
                    {
                        txns.Add(tx);
                        Debug.WriteLine("  containing tx {}", tx.getHashAsString());
                    }
                }
                foreach (Transaction t in txns)
                {
                    try
                    {
                        receive(t, b, BlockChain.NewBlockType.BEST_CHAIN, true);
                    }
                    catch (ScriptException e)
                    {
                        throw e;  // Cannot happen as these blocks were already verified.
                    }
                }
            }

            // Find the transactions that didn't make it into the new chain yet. For each input, try to connect it to the
            // transactions that are in {spent,unspent,pending}. Check the status of each input. For inactive
            // transactions that only send us money, we put them into the inactive pool where they sit around waiting for
            // another re-org or re-inclusion into the main chain. For inactive transactions where we spent money we must
            // put them back into the pending pool if we can reconnect them, so we don't create a double spend whilst the
            // network heals itself.
            Dictionary<Sha256Hash, Transaction> pool = new Dictionary<Sha256Hash, Transaction>();
            foreach (var item in unspent) pool.Add(item.Key, item.Value);    // TODO: ok?
            foreach (var item in spent) pool.Add(item.Key, item.Value);
            foreach (var item in pending) pool.Add(item.Key, item.Value);
            //pool.putAll(unspent);
            //pool.putAll(spent);
            //pool.putAll(pending);
            Dictionary<Sha256Hash, Transaction> toReprocess = new Dictionary<Sha256Hash, Transaction>();
            foreach (var item in onlyOldChainTransactions) toReprocess.Add(item.Key, item.Value);    // TODO: ok?
            foreach (var item in pending) toReprocess.Add(item.Key, item.Value);    // TODO: ok?
            //toReprocess.putAll(onlyOldChainTransactions);
            //toReprocess.putAll(pending);
            Debug.WriteLine("Reprocessing:");
            // Note, we must reprocess dead transactions first. The reason is that if there is a double spend across
            // chains from our own coins we get a complicated situation:
            //
            // 1) We switch to a new chain (B) that contains a double spend overriding a pending transaction. It goes dead.
            // 2) We switch BACK to the first chain (A). The dead transaction must go pending again.
            // 3) We resurrect the transactions that were in chain (B) and assume the miners will start work on putting them
            //    in to the chain, but it's not possible because it's a double spend. So now that transaction must become
            //    dead instead of pending.
            //
            // This only occurs when we are double spending our own coins.
            foreach (Transaction tx in dead.Values)
            {
                reprocessTxAfterReorg(pool, tx);
            }
            foreach (Transaction tx in toReprocess.Values)
            {
                reprocessTxAfterReorg(pool, tx);
            }

            Debug.WriteLine("post-reorg balance is {}", Utils.bitcoinValueToFriendlyString(getBalance()));

            // Inform event listeners that a re-org took place.
            foreach (WalletEventListener l in eventListeners)
            {
                // Synchronize on the event listener as well. This allows a single listener to handle events from
                // multiple wallets without needing to worry about being thread safe.
                lock (l)
                {
                    l.onReorganize();
                }
            }
        }

        private void reprocessTxAfterReorg(Dictionary<Sha256Hash, Transaction> pool, Transaction tx)
        {
            Debug.WriteLine("  TX {}", tx.getHashAsString());
            int numInputs = tx.inputs.Count;
            int noSuchTx = 0;
            int success = 0;
            bool isDead = false;
            foreach (TransactionInput input in tx.inputs)
            {
                if (input.isCoinBase())
                {
                    // Input is not in our wallet so there is "no such input tx", bit of an abuse.
                    noSuchTx++;
                    continue;
                }
                TransactionInput.ConnectionResult result = input.connect(pool, false);
                if (result == TransactionInput.ConnectionResult.SUCCESS)
                {
                    success++;
                }
                else if (result == TransactionInput.ConnectionResult.NO_SUCH_TX)
                {
                    noSuchTx++;
                }
                else if (result == TransactionInput.ConnectionResult.ALREADY_SPENT)
                {
                    isDead = true;
                    // This transaction was replaced by a double spend on the new chain. Did you just reverse
                    // your own transaction? I hope not!!
                    Debug.WriteLine("   ->dead, will not confirm now unless there's another re-org", tx.getHashAsString());
                    TransactionOutput doubleSpent = input.getConnectedOutput(pool);
                    Transaction replacement = doubleSpent.getSpentBy().parentTransaction;
                    dead.Add(tx.getHash(), tx);
                    // Inform the event listeners of the newly dead tx.
                    foreach (WalletEventListener listener in eventListeners)
                    {
                        lock (listener)
                        {
                            listener.onDeadTransaction(tx, replacement);
                        }
                    }
                    break;
                }
            }
            if (isDead) return;

            if (noSuchTx == numInputs)
            {
                Debug.WriteLine("   ->inactive", tx.getHashAsString());
                inactive.Add(tx.getHash(), tx);
            }
            else if (success == numInputs - noSuchTx)
            {
                // All inputs are either valid for spending or don't come from us. Miners are trying to reinclude it.
                Debug.WriteLine("   ->pending", tx.getHashAsString());
                pending.Add(tx.getHash(), tx);
            }
        }

        /**
         * Returns an immutable view of the transactions currently waiting for network confirmations.
         */
        //TODO: public Collection<Transaction> getPendingTransactions()
        //{
        //    return Collections.unmodifiableCollection(pending.values());
        //}
    }
}
